#include "MysqlConnPool.h"
#include <chrono>
#include <iostream>
using std::cout;
using std::endl;
MysqlConnPool *MysqlConnPool::getMysqlConnPool()
{
    static MysqlConnPool sqlPool;
    return &sqlPool;
}

MysqlConnPool::MysqlConnPool()
    : _ip("127.0.0.1"),
      _user("zrj"),
      _passwd("zrj123"),
      _dbName("test"),
      _port(3306),
      _minSize(10),
      _maxSize(50),
      _curSize(0),
      _timeout(1000),
      _maxIdleTime(5000),
      _isRunning(true)
{
    for (int i = 0; i < _minSize; ++i)
    {
        addConnection();
    }
    thread producer(&MysqlConnPool::produceConnection, this);
    thread recycler(&MysqlConnPool::recycleConnection, this);
    producer.detach();
    recycler.detach();
}

void MysqlConnPool::addConnection()
{
    MysqlConn *conn = new MysqlConn;
    conn->connect(_user, _passwd, _dbName, _ip, _port);
    conn->refreshAliveTime();
    _connections.push(conn);
    ++_curSize;
}

void MysqlConnPool::produceConnection()
{
    while (_isRunning)
    {
        unique_lock<mutex> locker(_mtx);
        while (_isRunning && (_connections.size() >= _minSize || _curSize >= _maxSize))
        {
            _condProduce.wait(locker);
        }
        if(!_isRunning){
            break;
        }
        addConnection();
        _condConsume.notify_all();
    }
}

void MysqlConnPool::recycleConnection()
{
    while (_isRunning)
    {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        lock_guard<mutex> locker(_mtx);
        while (_connections.size() > _minSize)
        {
            MysqlConn *conn = _connections.front();
            if (conn->getAliveTime() >= _maxIdleTime)
            {
                _connections.pop();
                delete conn;
                --_curSize;
            }
            else
            {
                break;
            }
        }
    }
}

shared_ptr<MysqlConn> MysqlConnPool::getConnection()
{
    unique_lock<mutex> locker(_mtx);
    while (_connections.empty())
    {
        if (std::cv_status::timeout == _condConsume.wait_for(locker, std::chrono::milliseconds(_timeout)))
        {
            if (_connections.empty())
            {
                continue;
                // return nullptr;
            }
        }
    }
    shared_ptr<MysqlConn> connptr(_connections.front(), [this](MysqlConn *conn)
                                  {
        lock_guard<mutex> locker(_mtx);
        conn->refreshAliveTime();
        _connections.push(conn); });
    _connections.pop();
    _condProduce.notify_all();
    return connptr;
}

MysqlConnPool::~MysqlConnPool()
{
    while (!_connections.empty())
    {
        MysqlConn *conn = _connections.front();
        _connections.pop();
        delete conn;
    }
}

void MysqlConnPool::exit()
{
    _isRunning = false;
    _condProduce.notify_all();
}